<?php

namespace App\Common\Service;

use App\Common\Model\Attribute\CreateSql;
use App\Common\Model\Attribute\InitData;
use App\Common\Model\Model;
use App\Common\Util\TP;
use Sc\Util\ImitateAopProxy\AopProxyTrait;
use Sc\Util\Tool;
use think\facade\Db;

/**
 * Class TableInitSqlService
 */
class TableInitSqlService
{
    use AopProxyTrait;

    private array $createSql = [];
    private array $initData = [];

    /**
     * @return array
     */
    public function getCreateSql(): array
    {
        return $this->createSql;
    }

    /**
     * @return array
     */
    public function getInitData(): array
    {
        return $this->initData;
    }

    /**
     * @param array $errorMsg
     *
     * @return void
     */
    public function runCreateSql(array &$errorMsg): void
    {
        foreach ($this->createSql as $sql) {
            try {
                Db::query($sql);
            } catch (\Throwable $throwable) {
                $errorMsg[] = "create table: " . $throwable->getMessage();
            }
        }
    }

    /**
     * @param array $errorMsg
     *
     * @return void
     */
    public function runInitData(array &$errorMsg): void
    {
        $prefix = Model::getPrefix();

        foreach ($this->initData as $table => $data) {
            try {
                if (count($data) === count($data, 1)) {
                    array_map(fn($sql) => Db::execute(preg_replace('/INSERT INTO `[a-zA-Z]+_/', "INSERT INTO `{$prefix}", $sql)), $data);
                }else{
                    Db::table($table)->insertAll($data);
                }
            } catch (\Throwable $throwable) {
                $errorMsg[] = "init data($table): " . $throwable->getMessage();
                $errorMsg[] = json_encode($data);
            }
        }
    }

    /**
     * @param string $dir
     *
     * @return TableInitSqlService
     * @throws \Exception
     */
    public function extractInitData(string $dir): static
    {
        Tool::dir($dir)->each(function (Tool\Dir\EachFile $file){
            $content = file_get_contents($file->filepath);
            if (!str_contains($content, '#[CreateSql') && !str_contains($content, '#[InitData')) {
                return;
            }

            preg_match('/^namespace ([\w\\\]+);\s*$/m', $content, $match);
            $modelNamespace = $match[1];
            $classname      = $modelNamespace . '\\' . strtr($file->filename, ['.php' => '']);
            $class          = new $classname();

            $reflectionClass = new \ReflectionClass($class);
            $prefix = Model::getPrefix();

            if ($CreateSqlAttributes = $reflectionClass->getAttributes(CreateSql::class)) {
                $this->createSql[]  = preg_replace('/CREATE\s+TABLE\s+`[a-z0-9]+_(\w+)`/', sprintf("CREATE TABLE IF NOT EXISTS `%s$1`", $prefix ?: 'sc_'), $CreateSqlAttributes[0]->newInstance()->sql);
            }

            if ($InitDataAttributes = $reflectionClass->getAttributes(InitData::class)) {
                $table  = $class->getTable();
                if (!$prefix) {
                    $table = 'sc_' . $table;
                }
                $this->initData[$table] = $InitDataAttributes[0]->newInstance()->data;
            }
        });

        return $this;
    }
}